package com.platform.comm.web.advice;

import com.platform.comm.exceptions.FeignApiException;
import com.platform.comm.exceptions.RestApiException;
import com.platform.comm.exceptions.ValidationException;
import com.platform.comm.web.results.DataJsonResult;
import com.platform.comm.web.results.EsSearchResult;
import com.platform.comm.web.results.JsonResult;
import com.platform.comm.web.results.PageListJsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.sql.SQLException;
import java.util.stream.Collectors;

/**
 * Description: 全局Controller异常
 *
 * @author libin
 * Created on 2019/4/18
 **/
@Slf4j
@RestControllerAdvice
public class DefaultRestControllerAdvice implements ResponseBodyAdvice<Object> {

    @ExceptionHandler(SQLException.class)
    public JsonResult handleSqlException(SQLException ex) {
        JsonResult result = JsonResult.of();
        log.error(ex.getMessage(), ex);
        return result.fail("数据库操作异常");
    }

    @ExceptionHandler(DataAccessException.class)
    public JsonResult handleDataAccessException(DataAccessException ex) {
        JsonResult result = JsonResult.of();
        log.error(ex.getMessage(), ex);
        return result.fail("数据库操作异常");
    }

    /**
     * REST接口通用异常。
     * 返回指定的HTTP状态码，以异常消息作为响应文本
     */
    @ExceptionHandler(RestApiException.class)
    public JsonResult handleIllegalParamEx(RestApiException ex) {
        log.error(ex.getMessage(), ex);
        JsonResult result = JsonResult.of();
        if (ex.getStatusCode() == RestApiException.SC_REQUEST_RANGE_NOT_SATISFIABLE) {
            return result.apply(String.valueOf(ex.getStatusCode()), ex.getMessage());
        }
        return result.fail(ex.getMessage());
    }

    /**
     * 参数验证
     *
     * @param ex 异常
     * @return JsonResult
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public JsonResult handleValidEx(MethodArgumentNotValidException ex) {
        log.error(ex.getMessage(), ex);
        return JsonResult.of(ex.getBindingResult());
    }

    /**
     * 400 - (Validation failed)
     */
    @ExceptionHandler(BindException.class)
    public JsonResult handleBindException(BindException ex) {
        log.error(ex.getMessage(), ex);
        return JsonResult.of(ex.getBindingResult());
    }

    /**
     * 参数验证
     *
     * @param ex 异常
     * @return JsonResult
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public JsonResult handleNotFound(NoHandlerFoundException ex) {
        log.error(ex.getMessage(), ex);
        JsonResult result = JsonResult.of();
        return result.fail(ex.getMessage());
    }

    @ExceptionHandler(FeignApiException.class)
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    public JsonResult handleFeign(FeignApiException ex){
        log.error(ex.getMessage(), ex);
        JsonResult result = JsonResult.of();
        return result.fail(ex.getMessage());
    }


    /**
     * 请求的 URL 参数检验
     *
     * @param ex 异常信息
     * @return 返回提示信息
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public JsonResult handleBindException2(ConstraintViolationException ex) {
        String errors = ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage)
                .collect(Collectors.joining(";"));
        JsonResult result = JsonResult.of();
        log.error(ex.getMessage(), ex);
        return result.fail(errors);
    }

    /**
     * 请求的 URL 参数检验
     *
     * @param ex 异常信息
     * @return 返回提示信息
     */
    @ExceptionHandler(ValidationException.class)
    public JsonResult handleValidationException(ValidationException ex) {
        String message = ex.getMessage().replaceFirst("\\d+:", "");
        log.error(message);
        JsonResult result = JsonResult.of();
        return result.fail(message);
    }


    @ExceptionHandler(MissingServletRequestParameterException.class)
    public JsonResult handleMissingParamEx(MissingServletRequestParameterException ex) {
        log.error(ex.getMessage(), ex);
        JsonResult result = JsonResult.of();
        return result.fail("缺少请求参数信息：".concat(ex.getParameterName()));
    }

    /**
     * 参数校验异常 - (Missing request parameter)
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public JsonResult handleIllegalStateException(IllegalArgumentException ex) {
        log.error(ex.getMessage(), ex);
        JsonResult result = JsonResult.of();
        return result.fail("缺少请求参数信息：".concat(ex.getMessage()));
    }

    /**
     * 500 - Server Error
     */
    @ExceptionHandler
    public JsonResult handleUnexpectedException(Exception ex) {
        log.error(ex.getMessage(), ex);
        JsonResult result = JsonResult.of();
        String userPwdErr = "用户名或密码错误";
        if(userPwdErr.equals(ex.getMessage())){
            return result.fail(ex.getMessage());
        }
        return result.fail("服务器异常：".concat(ex.getMessage()));
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    /**
     * 响应参数封装
     *
     * @param o                  返回内容
     * @param methodParameter    方法参数
     * @param mediaType          类型
     * @param aClass             转换类
     * @param serverHttpRequest  请求
     * @param serverHttpResponse 响应
     * @return 返回封装后内容
     */
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
                                  Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {
        if (o instanceof PageListJsonResult<?>) {
            PageListJsonResult<?> data = (PageListJsonResult<?>) o;
            return DataJsonResult.succeed(data);
        } else if (o instanceof EsSearchResult<?>) {
            EsSearchResult<?> data = (EsSearchResult<?>) o;
            return DataJsonResult.succeed(data);
        } else {
            return o;
        }
    }
}
